/*
 * ringbuffer.c
 *
 *  Created on: 2021年6月6日
 *      Author: wangbing
 *      Email : mz8023yt@163.com
 */

#ifdef __cplusplus
extern "C"
{
#endif

#include <stdio.h>              // printf
#include <stdint.h>             // uint8_t
#include <string.h>             // memcpy
#include <stdlib.h>             // malloc
#include <unistd.h>             // write
#include <fcntl.h>              // O_CREAT

#include "ringbuffer.h"

/**
 * @brief 队列初始化
 */
int ringbuffer_init(RINGBUFFER *rbuf, uint32_t size)
{
    if(NULL == rbuf || 0 == size)
    {
        return EINVAL;
    }

    if(rbuf->init)
    {
        return EREINIT;
    }

    rbuf->r = 0;
    rbuf->w = 0;
    rbuf->size = size;
    rbuf->addr = malloc(size);
    if(NULL == rbuf->addr)
    {
        return ENOSPC;
    }

    if (pthread_mutex_init(&rbuf->lock, NULL))
    {
        free(rbuf->addr);
        return EINVAL;
    }

    rbuf->init = 1;

    return 0;
}

/**
 * @brief 队列销毁
 */
int ringbuffer_deinit(RINGBUFFER *rbuf)
{
    if(NULL == rbuf)
    {
        return EINVAL;
    }

    if(!rbuf->init)
    {
        return ENOTINIT;
    }

    pthread_mutex_destroy(&rbuf->lock);
    free(rbuf->addr);
    rbuf->init = 0;

    return 0;
}

/**
 * @brief 队列读取
 */
int ringbuffer_read(RINGBUFFER *rbuf, uint8_t *obuf, uint32_t olen)
{
    uint32_t fill = 0;
    uint32_t behind_fill = 0;

    if(NULL == rbuf || NULL == obuf || 0 == olen)
    {
        return EINVAL;
    }

    if(!rbuf->init)
    {
        return ENOTINIT;
    }

    if(olen > rbuf->size)
    {
        return ERANGE;
    }

    /* 判断数据是否就绪 */
    fill = RINGBUFFER_FILL_LEN(rbuf);
    if(fill < olen)
    {
        return ENODAT;
    }

    /* 判断是否需要回环读取 */
    behind_fill = RINGBUFFER_BEHIND_FILL_LEN(rbuf);
    if(behind_fill >= olen)
    {
        /* 无需回环, 直接读取 */
        memcpy(obuf, rbuf->addr + rbuf->r, olen);
    }
    else
    {
        /* 需要回环, 分两步读 */
        uint32_t len1 = behind_fill;
        uint32_t len2 = olen - len1;
        memcpy(obuf, rbuf->addr + rbuf->r, len1);
        memcpy(obuf + len1, rbuf->addr, len2);
    }

    /* 更新读指针 */
    rbuf->r = RINGBUFFER_UPDATE(rbuf->r, olen, rbuf->size);

    return 0;
}

/**
 * @brief 写入数据到队列
 */
int ringbuffer_write(RINGBUFFER *rbuf, uint8_t *ibuf, uint32_t ilen)
{
    uint32_t lost = 0;
    uint32_t idle = 0;
    uint32_t behind_idle = 0;
    uint32_t len1 = 0;
    uint32_t len2 = 0;

    if(NULL == rbuf || NULL == ibuf || 0 == ilen)
    {
        return EINVAL;
    }

    if(!rbuf->init)
    {
        return ENOTINIT;
    }

    if(ilen > rbuf->size)
    {
        return EINVAL;
    }

    /* 判断是否存在丢帧 */
    idle = RINGBUFFER_IDLE_LEN(rbuf);
    lost = ilen > idle;

#if RINGBUFFER_LOST_WAY == RINGBUFFER_LOST_LATEST
    if(lost)
    {
        return ENOSPC;
    }
#endif

    /* 判断是否需要回环写入 */
    behind_idle = RINGBUFFER_BEHIND_IDLE_LEN(rbuf);
    if(behind_idle >= ilen)
    {
        /* 无需回环, 直接写入 */
        memcpy(rbuf->addr + rbuf->w, ibuf, ilen);
    }
    else
    {
        /* 需要回环, 分两步写 */
        len1 = behind_idle;
        len2 = ilen - len1;
        memcpy(rbuf->addr + rbuf->w, ibuf, len1);
        memcpy(rbuf->addr, ibuf + len1, len2);
    }

    /* 更新写指针 */
    rbuf->w = RINGBUFFER_UPDATE(rbuf->w, ilen, rbuf->size);

#if RINGBUFFER_LOST_WAY == RINGBUFFER_LOST_OLDEST
    if(lost)
    {
        rbuf->r = RINGBUFFER_UPDATE(rbuf->w, 1, rbuf->size);
        return ECOVER;
    }
#endif

    return 0;
}

/**
 * @brief 写入数据到队列(包含数据头)
 */
int ringbuffer_write_with_header(RINGBUFFER *rbuf, uint8_t *hbuf, uint32_t hlen, uint8_t *ibuf, uint32_t ilen)
{
    int ret = 0;

    if(NULL == rbuf || NULL == hbuf || NULL == ibuf || 0 == hlen || 0 == ilen)
    {
        return EINVAL;
    }

    if(!rbuf->init)
    {
        return ENOTINIT;
    }

    ret = ringbuffer_lock(rbuf);
    if(ret)
    {
        return ret;
    }

    ret = ringbuffer_write(rbuf, hbuf, hlen);
    if(ret)
    {
        return ret;
    }

    ret = ringbuffer_write(rbuf, ibuf, ilen);
    if(ret)
    {
        return ret;
    }

    ret = ringbuffer_unlock(rbuf);
    if(ret)
    {
        return ret;
    }

    return 0;
}

/**
 * @brief 判空
 */
bool ringbuffer_is_empty(RINGBUFFER *rbuf)
{
    int fill = 0;

    if(NULL == rbuf)
    {
        return EINVAL;
    }

    if(!rbuf->init)
    {
        return ENOTINIT;
    }

    fill = RINGBUFFER_FILL_LEN(rbuf);

    return (0 == fill) ? true : false;
}

/**
 * @brief 判满
 */
bool ringbuffer_is_full(RINGBUFFER *rbuf)
{
    int idle = 0;

    if(NULL == rbuf)
    {
        return EINVAL;
    }

    if(!rbuf->init)
    {
        return ENOTINIT;
    }

    idle = RINGBUFFER_IDLE_LEN(rbuf);

    return (0 == idle) ? true : false;
}

/**
 * @brief 获取缓冲池空闲大小
 */
uint32_t ringbuffer_idle_length_get(RINGBUFFER *rbuf)
{
    return RINGBUFFER_IDLE_LEN(rbuf);
}

/**
 * @brief 获取缓冲池填充大小
 */
uint32_t ringbuffer_fill_length_get(RINGBUFFER *rbuf)
{
    return RINGBUFFER_FILL_LEN(rbuf);
}

/**
 * @brief 上锁
 */
int ringbuffer_lock(RINGBUFFER *rbuf)
{
    if(NULL == rbuf)
    {
        return EINVAL;
    }

    if(!rbuf->init)
    {
        return ENOTINIT;
    }

    pthread_mutex_lock(&rbuf->lock);
    return 0;
}

/**
 * @brief 尝试上锁
 */
int ringbuffer_trylock(RINGBUFFER *rbuf)
{
    if(NULL == rbuf)
    {
        return EINVAL;
    }

    if(!rbuf->init)
    {
        return ENOTINIT;
    }

    return pthread_mutex_trylock(&rbuf->lock);
}

/**
 * @brief 解锁
 */
int ringbuffer_unlock(RINGBUFFER *rbuf)
{
    if(NULL == rbuf)
    {
        return EINVAL;
    }

    if(!rbuf->init)
    {
        return ENOTINIT;
    }

    pthread_mutex_unlock(&rbuf->lock);
    return 0;
}

/**
 * @brief 清空队列
 */
int ringbuffer_clean(RINGBUFFER *rbuf)
{
    if(NULL == rbuf)
    {
        return EINVAL;
    }

    if(!rbuf->init)
    {
        return ENOTINIT;
    }

    rbuf->r = 0;
    rbuf->w = 0;
    memset(rbuf->addr, 0, rbuf->size);

    return 0;
}

/**
 * @brief 打印队列数据
 */
int ringbuffer_print(RINGBUFFER *rbuf)
{
    uint8_t i = 0;
    uint8_t j = 0;
    uint8_t col = 32;                           // 每行显示多少列数据
    uint8_t row = rbuf->size / col;             // 总共显示多少行
    uint8_t ch = 0;

    for (i = 0; i < row; i++)
    {
        printf("%08x  ", i * col);
        for (j = 0; j < col; j++)
        {
            printf("%02x ", rbuf->addr[i * col + j]);
            if (j % 8 == 7)
                printf(" ");
        }

        printf("|");
        for (j = 0; j < col; j++)
        {
            ch = rbuf->addr[i * col + j];

            if ('\t' == ch)
                ch = '>';
            else if ('\b' == ch)
                ch = '<';
            else if ('\r' == ch)
                ch = '\\';
            else if ('\n' == ch)
                ch = '\\';
            else if (0x03 == ch)
                ch = '^';
            else if (ch < 32 || ch >= 127)
                ch = '.';

            printf("%c", ch);
        }
        printf("|\r\n");
    }

    return 0;
}

void printn(char c, int num)
{
    int i = 0;

    if(num <= 0)
    {
        return;
    }

    for(i = 0; i < num; i++)
    {
        printf("%c", c);
    }
    fflush(stdout);
}

/**
 * @brief 打印队列状态信息
 */
int ringbuffer_status(RINGBUFFER *rbuf)
{
    int w = rbuf->w;
    int r = rbuf->r;
    int size = rbuf->size;
    int length = 50;
    int wlocat = w * length / size;
    int rlocat = r * length / size;
    int locat1 = 0;
    int locat2 = 0;
    int locat3 = 0;

    locat1 = (w > r) ? rlocat : wlocat;
    locat2 = (w > r) ? wlocat - rlocat : rlocat - wlocat;
    locat3 = (w > r) ? length - wlocat : length - rlocat;

    /* 第一行 */
    printn(' ', wlocat);
    printn('W', 1);
    printn(' ', length - wlocat - 1);
    printn(' ', 3);
    printf("WIDX = %d\n", w);

    /* 第二行 */
    printn('-', wlocat);
    printn('+', 1);
    printn('-', length - wlocat - 1);
    printn(' ', 3);
    printf("RIDX = %d\n", r);

    /* 第三行 */
    if(w > r)
    {
        printn(' ', locat1);
        printn('X', locat2);
        printn(' ', locat3);
    }
    else if(r > w)
    {
        printn('X', locat1);
        printn(' ', locat2);
        printn('X', locat3);
    }
    else
    {
        printn(' ', length);
    }
    printn(' ', 3);
    printf("SIZE = %d\n", size);

    /* 第四行 */
    printn('-', rlocat);
    printn('+', 1);
    printn('-', length - rlocat - 1);
    printn(' ', 3);
    printf("IDLE = %d\n", RINGBUFFER_IDLE_LEN(rbuf));

    /* 第五行 */
    printn(' ', rlocat);
    printn('R', 1);
    printn(' ', length - rlocat - 1);
    printn(' ', 3);
    printf("FILL = %d\n", RINGBUFFER_FILL_LEN(rbuf));

    return 0;
}

/**
 * @brief 导出队列数据到本地文件
 */
int ringbuffer_dump(RINGBUFFER *rbuf)
{
    int fd = 0;
    int wcnt = 0;
    char *name = "dump.bin";
    uint32_t scode = 0xFF5AFF01;            // start code
    uint32_t dcode = 0xFF5AFF02;            // data code
    uint32_t ecode = 0xFF5AFFFE;            // end code

    fd = open(name, O_RDWR |  O_CREAT, 0777);
    if(fd < 0)
    {
        printf("err: open %s failed", name);
        close(fd);
        return ERR;
    }

    wcnt = write(fd, &scode, sizeof(uint32_t));
    if(wcnt != sizeof(uint32_t))
    {
        printf("err: write scode failed!");
        close(fd);
        return ERR;
    }

    wcnt = write(fd, rbuf, sizeof(RINGBUFFER));
    if(wcnt != sizeof(RINGBUFFER))
    {
        printf("err: write rbuf struct failed!");
        close(fd);
        return ERR;
    }

    wcnt = write(fd, &dcode, sizeof(uint32_t));
    if(wcnt != sizeof(uint32_t))
    {
        printf("err: write dcode failed!");
        close(fd);
        return ERR;
    }

    wcnt = write(fd, rbuf->addr, rbuf->size);
    if(wcnt != rbuf->size)
    {
        printf("err: write rbuf failed!");
        close(fd);
        return ERR;
    }

    wcnt = write(fd, &ecode, sizeof(uint32_t));
    if(wcnt != sizeof(uint32_t))
    {
        printf("err: write ecode failed!");
        close(fd);
        return ERR;
    }

    close(fd);

    return 0;
}

#ifdef __cplusplus
}
#endif
